//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<event_center.cpp>
///	@path	~/src/engine/xcore/
///	@date	2007/11/08
///	@desc	Event processing center.

#include "config/config.h"

#include "lib/system/thread_debug.h"

#include "engine/xcore/event_center.h"

namespace xeres {

	// ctor
	EventCenter::EventCenter( void )
		:m_eventId(0)
	{
	}

	// dtor
	EventCenter::~EventCenter( void )
	{
		assert( m_eventTree.empty() );
	}

	// Initialize
	void EventCenter::Initialize( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main") );

		// initialize event tree, insert root for placeholder
		*m_eventTree.open_node( _S(".") , true ) = NULL;
		*m_eventTree.open_node( _S("input") , true ) = NULL;
		*m_eventTree.open_node( _S("message") , true ) = NULL;
		*m_eventTree.open_node( _S("network") , true ) = NULL;
		*m_eventTree.open_node( _S("user") , true ) = NULL;
	}

	// Finalize
	void EventCenter::Finalize( void )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main") );

		// cleanup
		for( dir_tree< _EventNode * >::iterator it = m_eventTree.begin() , end = m_eventTree.end() ;
			it != end ; ++it )
		{
			_EventNode * node = *it;

			if( node )
			{
				TRACE_INFO( _S("Releasing event node '%s'...") , it.name().c_str() );

				for( size_t i = 0 , total = node->m_plugs.size() ; i < total ; ++i )
				{
					_EventPlug& plug = node->m_plugs[i];
					if( plug.m_released != NULL )
					{
						plug.m_released();
					}
				}
				delete node;
			}
		}
		m_eventTree.clear();
	}

	// active callback
	void EventCenter::ActiveCallback( void * data , void * eventParam )
	{
		_EventNode * node = static_cast<_EventNode*>( data );

		TRACK_FUNCTION_SCOPE_MSG( _S("Event<%s>") , node->m_path.c_str() );
		RUNNING_ON_THREAD( _S("main|tick|!_render") );

		// iterate all plugs and execute them in order
		bool called = false;
		for( size_t i = 0 , total = node->m_plugs.size() ; i < total ; ++i )
		{
			_EventPlug& plug = node->m_plugs[i];
			if( plug.m_call != NULL )
			{
				called = true;
				if( plug.m_call( eventParam ) == EVENT_HANDLED )
				{
					if( !node->m_iterateAllPlug )
						return;
				}
			}
		}

		if( !called )
		{
			// run unhandled
			node->m_event->OnUnhandled( eventParam );
		}
	}

	// register event
	bool EventCenter::RegisterEvent( const WString& path , RefWeak<IEvent> event , bool iterate_all )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!_render") );

		assert( event.IsValid() );

		if( m_eventTree.open_node( path ) )
		{
			TRACE_ERROR( _S("EventCenter::RegisterEvent: '%s' event entry existed.") , path.c_str() );
			return false;
		}

		_EventNode * en = new _EventNode;
		en->m_event = event;
		en->m_path = path;
		en->m_iterateAllPlug = iterate_all;

		*( m_eventTree.open_node( path , true ) ) = en;

		event->OnRegister( bind( ActiveCallback , en , _1 ) );
		return true;
	}

	// release event
	bool EventCenter::ReleaseEvent( const WString& path )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!_render") );

		_EventNode * node = *m_eventTree.open_node( path );
		if( node )
		{
			TRACE_INFO( _S("Releasing event node '%s'...") , path.c_str() );
			for( size_t i = 0 , total = node->m_plugs.size() ; i < total ; ++i )
			{
				_EventPlug& plug = node->m_plugs[i];
				if( plug.m_released != NULL )
				{
					plug.m_released();
				}
			}
			delete node;
			m_eventTree.del_node( path );

			return true;
		}
		return false;
	}

	// subscribe event
	EventID EventCenter::SubscribeEvent( const WString& path , EventSlot slot ,
			function<HandleEvent(void*)> subscriber , function<void(void)> released )
	{
		TRACK_FUNCTION_SCOPE();
		RUNNING_ON_THREAD( _S("main|tick|!_render") );

		_EventNode * en = *m_eventTree.open_node( path );
		if( en )
		{
			_EventPlug plug;
			plug.m_call = subscriber;
			plug.m_released = released;
			plug.m_id = m_eventId;
			++m_eventId;

			for( size_t i = 0 , total = en->m_plugs.size() ; i < total ; ++i )
			{
				_EventPlug& p = en->m_plugs[i];
				if( p.m_slot > slot )
				{
					// insert before higher level slot
					if( i == 0 )
					{
						en->m_plugs.push_front( plug );
					}
					else
					{
						en->m_plugs.insert( en->m_plugs.begin() + i - 1 , plug );
					}
					return plug.m_id;
				}
			}
			en->m_plugs.push_back( plug );
			return plug.m_id;
		}
		TRACE_ERROR( _S("EventCenter::SubscribeEvent: Event '%s' not found.") , path.c_str() );
		return INVALID_EVENT_ID;
	}

} // namespace xeres
